﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace EdiNet
{
    public class DataElement
    {
        const int INITIAL_ELEMENT_COUNT = 5;
        string simpleValue;
        List<string> elementValues;

        /// <summary>
        /// Initializes a new instance of the DataElement class with empty value
        /// </summary>
        public DataElement()
            : this(string.Empty)
        {
        }

        public DataElement(params string[] elements)
            : this(elements[0])
        {
            for (int i = 1; i < elements.Length; ++i)
            {
                Add(elements[i]);
            }
        }
        /// <summary>
        /// Initializes a new instance of the DataElement class with the specified value
        /// </summary>
        /// <param name="value"></param>
        public DataElement(string value)
        {
            this.simpleValue = value ?? string.Empty;
        }

        /// <summary>
        /// Adds a new component data element to the composite. 
        /// </summary>
        /// <param name="elementValue"></param>
        public void Add(string elementValue)
        {
            string val = elementValue ?? string.Empty;
            CreateElementValuesList();
            elementValues.Add(val);
        }

        /// <summary>
        /// Gets the simple data element value at a specified position. 
        /// </summary>
        /// <param name="position"></param>
        /// <returns></returns>
        public string this[int position]
        {
            get
            {
                if (position == 0)
                    return simpleValue;

                if (null == elementValues || position - 1 >= elementValues.Count())
                    return string.Empty;

                return elementValues[position - 1];
            }
            set
            {
                string val = value ?? string.Empty;
                if (position == 0)
                {
                    simpleValue = val;
                    return;
                }
                CreateElementValuesList();
                --position; //first element is stored in the simple value                
                for (; position > 0; --position)
                    elementValues.Add(string.Empty);
                elementValues.Add(val);

            }
        }

        /// <summary>
        /// Shortcut property. Gets value of a simple element or the first component element in a composite
        /// </summary>
        public string V
        {
            get { return simpleValue; }
            set
            {
                string val = value ?? string.Empty;
                simpleValue = val;
            }
        }

        /// <summary>
        /// Implicit conversion. Returns value of a simple element or the first component element in a composite
        /// </summary>
        public static implicit operator string(DataElement el)
        {
            return el.V;
        }


        /// <summary>
        /// Indicates whether the data element is simple or composite 
        /// </summary>
        /// <returns></returns>
        public bool IsSimpleDataElement()
        {
            return null == elementValues;
        }

        /// <summary>
        /// Returns the number of component elements in a composite. For simple elements returns 1
        /// </summary>
        /// <returns></returns>
        public int Count()
        {
            if (null == elementValues)
                return 1;
            return elementValues.Count() + 1;
        }

        /// <summary>
        /// Returns string representation of the data element. Uses a default component element separator. 
        /// /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return ToString(EdiReaderWriterSettings.DefaultX12.ComponentSeparatorChar);
        }

        /// <summary>
        /// Returns string representation of the data element. 
        /// </summary>
        /// <param name="componentElementSeparator"></param>
        /// <returns></returns>
        public string ToString(char componentElementSeparator)
        {
            if (IsSimpleDataElement())
                return simpleValue;
            StringBuilder res = new StringBuilder(simpleValue);
            res.Append(componentElementSeparator).Append(string.Join(componentElementSeparator.ToString(), elementValues));
            return res.ToString();
        }

        /// <summary>
        /// Compares data elements. Ignores whitespaces during comparison
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            DataElement el = obj as DataElement;
            if (object.ReferenceEquals(el, null))
                return false;
            if (V.Trim() != el.V.Trim())
                return false;
            if (IsSimpleDataElement() != el.IsSimpleDataElement())
                return false;

            if (IsSimpleDataElement())
                return true;

            return elementValues.Select(i => i.Trim()).SequenceEqual(el.elementValues.Select(i => i.Trim()));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="el"></param>
        /// <returns></returns>
        public static bool operator ==(DataElement el1, DataElement el2)
        {
            if (object.ReferenceEquals(el1, null))
                return object.ReferenceEquals(el2, null);

            return el1.Equals(el2);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="el1"></param>
        /// <param name="el2"></param>
        /// <returns></returns>
        public static bool operator !=(DataElement el1, DataElement el2)
        {
            return !(el1 == el2);
        }


        /// <summary>
        /// Returns hash code of the first element
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return simpleValue.GetHashCode();
        }


        /// <summary>
        /// Lasy list creation
        /// </summary>
        void CreateElementValuesList()
        {
            if (null == elementValues)
                elementValues = new List<string>(INITIAL_ELEMENT_COUNT);
        }
    }
}
